import JWT from "jsonwebtoken";
import { Next, Request, Response } from "restify";
import errors from "restify-errors";

import { config } from "@src/config";

export interface JwtPayload {
  card?: string;
  openId?: string;
  // TODO: consider adding WeChat Mini Program identifier

  iat: Date;
  exp: Date;
}

export const checkJwtC = (req: Request, res: Response, next: Next) => {
  if (!verifyJwt(req, res, next)) {
    return;
  }

  if (req.params.card === undefined) {
    next(new errors.InvalidCredentialsError("card"));

    return;
  }

  next();
};

export const checkJwtW = (req: Request, res: Response, next: Next) => {
  if (!verifyJwt(req, res, next)) {
    return;
  }

  if (req.params.openId === undefined) {
    next(new errors.InvalidCredentialsError("openId"));

    return;
  }

  next();
};

export const signJwt = (
  card: string | undefined = undefined,
  openId: string | undefined = undefined
) => {
  return JWT.sign(
    {
      card: card,
      openId: openId
    },
    config.jwt.secret,
    {
      expiresIn: config.jwt.expire
    }
  );
};

const verifyJwt = (req: Request, res: Response, next: Next): boolean => {
  if (!req.headers.authorization) {
    next(new errors.InvalidHeaderError("bearer"));

    return false;
  }

  const match = /^Bearer ([^ ]+)$/.exec(req.headers.authorization);
  if (!match) {
    next(new errors.InvalidHeaderError("bearer"));

    return false;
  }

  let payload: JwtPayload;
  try {
    payload = JWT.verify(match[1], config.jwt.secret) as JwtPayload;
  } catch (err) {
    next(new errors.InvalidHeaderError("jwt"));

    return false;
  }

  req.params.card = payload.card;
  req.params.openId = payload.openId;

  return true;
};
